﻿/*
 * MapIO.cs
 *
 * Created on November 17, 2007, 4:38 PM
 *
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 * Copyright (C) 2010       Mathijs Miermans
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
using System;
using System.IO;
using System.Collections.Generic;
using System.Windows;

using HexTD.hex;
using HexTD.util;
using HexTD.wave;

namespace HexTD.util
{
	public class MapIO
	{
#if PORT
    private static MD5 md5;
#endif

		public static String generateMapMD5(Context c)
		{
#if PORT
        if (MapIO.md5 == null) {
            try {
                MapIO.md5 = MD5.getInstance();
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(MapIO.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (MapIO.md5 != null) {
            return MapIO.md5.hashData(MapIO.printMap(c));
        } else {
            return "";
        }
#else
			return "";
#endif
		}

#if PORT_REM
		public static bool readLevel(Context c, String mapLocation)
		{
#if PORT
        URL url;
        bool success = true;
        c.mapMD5 = "";
        try {
            url = new URL(c.mapContextUrl, mapLocation);
            MapIO.readLevel(c, url, mapLocation);
        } catch (MalformedURLException ex) {
            System.Diagnostics.Debug.WriteLine("MapIO::readLevel: loading failed, context=" + c.mapContextUrl + " map=" + mapLocation);
            ex.printStackTrace();
            success = false;
        }
        // Check if md5 already calculated. If not, do it now.
        if (c.mapMD5.Length() == 0) {
            c.mapMD5 = MapIO.generateMapMD5(c);
        }
        return success;
#else
			return false;
#endif
		}

		public static void readLevel(Context c, string fetchUrl, String relativeLoc)
		{
#if PORT
        BufferedReader bufferedreader;

        try {
            //System.Diagnostics.Debug.WriteLine("HexTD::FetchFromUrl::file = " + fetchUrl.toString());
            URLConnection urlconnection = fetchUrl.openConnection();
            if (urlconnection.getContentEncoding() != null) {
                bufferedreader = new BufferedReader(new InputStreamReader(urlconnection.getInputStream(), urlconnection.getContentEncoding()));
            } else {
                bufferedreader = new BufferedReader(new InputStreamReader(urlconnection.getInputStream(), "utf-8"));
            }
        } catch (IOException _ex) {
            System.Diagnostics.Debug.WriteLine("HexTD::readFile:: Can't read from " + fetchUrl);
            return;
        }
        if (relativeLoc == null) {
            relativeLoc = fetchUrl.toString();
        }
        MapIO.readLevel(c, bufferedreader, relativeLoc);
#endif
		}
#endif

		public static bool readLevel(Context context, Stream stream, string location)
		{
			// We should not remove lots of graphical elements while painting is in progress, thus lock:
			lock (context.PaintingLock)
			{
				context.mapLocation = location;

				//this.gameBoard.setVisible(true);
				//this.jPanel_GameWon.setVisible(false);
				//this.jPanel_GameLost.setVisible(false);

				context.clearWaves();
				List<Wave> waves = context.getWaves();

				List<wave.Path> paths = context.getPaths();
				paths.Clear();
				context.livesStart = 20;
				context.interestIncrease = 3;
				context.interestStart = 3;
				context.levelInfo = "";
				context.levelTitle = "";
				context.backgroundImage = "";

				String sLine;
				char c;
				int i;
				int count;
				int lineNr = 0;
				try
				{

					bool next = true;
					int current = 0;
					long baseHealth = 100;
					int basePrice = 5;
					int pathNr = 0;
					PathNormal currentPath = null;
					Wave currentWave = null;
					String[] strings;

					using (TextReader tr = new StreamReader(stream))
					{
						while (next && (sLine = tr.ReadLine()) != null)
						{
							if (sLine.Trim().Length != 0)
							{
								switch (current)
								{
									case 4:
										context.levelInfo += sLine + Environment.NewLine;
										break;
									case 3:
										// We're reading a board
										count = 0;
										for (i = 0; i < sLine.Length; i++)
										{
											c = sLine[i];
											if (c != ' ')
											{
												if (count <= context.grid.GetLength(0) - (lineNr & 1))
												{
													switch (c)
													{
														case 'b':
															context.grid[count, lineNr] = new HexBase(count, lineNr, context, true);
															break;
														case 'e':
														case 'p':
															context.grid[count, lineNr] = new HexPath(count, lineNr, context);
															break;
														case 'n':
															context.grid[count, lineNr] = new HexEmpty(count, lineNr, context);
															break;
														default:
															System.Diagnostics.Debug.WriteLine("HexTD::readFile:: Unknown hex code: " + c);
															break;
													}
												}
												count++;
											}
										}
										lineNr++;
										break;
									case 2:
										// We're reading a wave
										strings = sLine.Split(new char[] {' '});
										if (strings.Length > 0)
										{
											currentWave.addEnemiesFromNames(strings, pathNr);
											pathNr++;
										}
										else
										{
											System.Diagnostics.Debug.WriteLine("Warning, empty wave: " + strings.Length + " enemies");
										}
										break;
									case 1:
										// We're reading a path
										strings = sLine.Split(new char[] {','});
										if (strings.Length == 2)
										{
											try
											{
												int x = int.Parse(strings[0]);
												int y = int.Parse(strings[1]);
												currentPath.addStep(x, y);
											}
											catch
											{
												System.Diagnostics.Debug.WriteLine("Warning, not a number in path!");
											}
										}
										else
										{
											System.Diagnostics.Debug.WriteLine("Warning, error reading path, expected 2 but got " + strings.Length + " values");
										}
										break;
									case 0:
										break;
									default:
										System.Diagnostics.Debug.WriteLine("Warning, unknown state (" + current + ") reading map");
										break;
								}
							}
							if (sLine.Trim().Length == 0)
							{
								switch (current)
								{
									case 1:
										// end path
										currentPath.finalise();
										paths.Add(currentPath);
										break;
									case 2:
										// end wave
										waves.Add(currentWave);
										break;
								}
								current = 0;
							}
							if (current == 0)
							{
								if (sLine.StartsWith("path"))
								{
									// start a new path
									strings = sLine.Split(new char[] {' '});
									float baseDelay = 0;
									if (strings.Length == 2)
									{
										try
										{
											baseDelay = float.Parse(strings[1]);
										}
										catch
										{
											System.Diagnostics.Debug.WriteLine("Warning, not a number in path baseDelay value!");
										}
									}
									current = 1;
									currentPath = new PathNormal(context, baseDelay);

								}
								else if (sLine.StartsWith("wave"))
								{
									// start a new wave
									current = 2;
									pathNr = 0;
									strings = sLine.Split(new char[] {' '});
									if (strings.Length == 3)
									{
										try
										{
											baseHealth = long.Parse(strings[1]);
											basePrice = int.Parse(strings[2]);
										}
										catch
										{
											System.Diagnostics.Debug.WriteLine("Warning, not a number in wave health value!");
										}
										currentWave = new Wave(context, baseHealth, basePrice, waves.Count + 1);
									}
									else
									{
										System.Diagnostics.Debug.WriteLine("Warning, error reading wave, expected 'wave <health> <price>' but got " + (strings.Length - 1) + " values");
									}
								}
								else if (sLine.StartsWith("level"))
								{
									// start of a new level!
									current = 3;
									context.levelTitle = sLine.Substring(6);
									sLine = tr.ReadLine();
									context.gridWidth = int.Parse(sLine);

									sLine = tr.ReadLine();
									context.gridHeight = int.Parse(sLine.Trim());

									context.grid = new Hex[context.gridWidth, context.gridHeight];

									lineNr = 0;
								}
								else if (sLine.StartsWith("budget"))
								{
									// start of a new level!
									strings = sLine.Split(new char[] { ' ' });
									if (strings.Length >= 2)
									{
										try
										{
											int budget = int.Parse(strings[1]);
											context.creditsStart = budget;
										}
										catch
										{
											System.Diagnostics.Debug.WriteLine("Warning, not a number in budget value!");
										}
									}
									else
									{
										System.Diagnostics.Debug.WriteLine("Warning, error reading budget, expected 'budget <budget> <baseInterest> <interestIncrease>' but got " + (strings.Length - 1) + " values");
									}
									if (strings.Length >= 4)
									{
										try
										{
											context.interestStart = int.Parse(strings[2]);
											context.interestIncrease = int.Parse(strings[3]);
										}
										catch
										{
											System.Diagnostics.Debug.WriteLine("Warning, not a number in budget value!");
										}
									}
								}
								else if (sLine.StartsWith("lives"))
								{
									// start of a new level!
									strings = sLine.Split(new char[] {' '});
									if (strings.Length == 2)
									{
										try
										{
											context.livesStart = int.Parse(strings[1]);
											context.resetLives(context.livesStart);
										}
										catch
										{
											System.Diagnostics.Debug.WriteLine("Warning, not a number in budget value!");
										}
									}
									else
									{
										System.Diagnostics.Debug.WriteLine("Warning, error reading lives, expected 'lives <lives>' but got " + (strings.Length - 1) + " values");
									}
								}
								else if (sLine.StartsWith("background"))
								{
									// start of a new level!
									context.backgroundImage = sLine.Substring(11);
								}
								else if (sLine.StartsWith("info"))
								{
									// Level info following
									current = 4;
								}
							}
						}
					}

					switch (current)
					{
						case 1:
							// end path
							currentPath.finalise();
							paths.Add(currentPath);
							break;
						case 2:
							// end wave
							waves.Add(currentWave);
							break;
					}
				}
				catch (Exception e)
				{
					System.Diagnostics.Debug.WriteLine("HexTD::readFile::IO exception: " + e.ToString());
					return false;
					// Do something with the error here.
				}
				//this.jPanelLevelSelector.setLevelInfo("<h1>" + this.levelTitle + "</h1>" + this.levelInfo);
				//this.jCheckBox_autoLaunch.setSelected(false);
				//this.recalculateScale();
			}
			//this.context.resetMap();
			//this.blackBoxRecord.clear();
			//this.updateInfo();
			return true;
		}

		public static string printMap(Context c)
		{
#if PORT
        String retval = "level " + c.levelTitle + "\n";
        retval += "" + c.gridWidth + "\n";
        retval += "" + c.gridHeight + "\n";
        for (int y = 0; y < c.grid.GetLength(1); y++) {
            if ((y & 1) == 1) {
                retval += " ";
            }
            int xLength = c.grid.GetLength(0) - (y & 1);
            for (int x = 0; x < xLength; x++) {
                retval += c.grid[x, y].getShortName() + " ";
            }
            retval += "\n";
        }
        retval += "\n";
        retval += "info\n";
        retval += c.levelInfo + "\n";
        retval += "\n";
        Vector<Path> paths = c.getPaths();
        for (int i = 0; i < paths.Count; i++) {
            Path tempPath = paths[i];
            retval += "path " + tempPath.getBaseDelay() + "\n";
            int[] step;
            for (int s = 0; s < tempPath.Length(); s++) {
                step = tempPath.getStepHexCoords(s);
                retval += "" + step[0] + "," + step[1] + "\n";
            }
            retval += "\n";
        }

        retval += "budget " + c.creditsStart + " " + c.interestStart + " " + c.interestIncrease + "\n";
        retval += "\n";
        retval += "lives " + c.livesStart + "\n";
        retval += "\n";
        retval += "background " + c.backgroundImage + "\n";
        retval += "\n";

        for (Iterator<Wave> i = c.getWaves().iterator(); i.hasNext();) {
            Wave w = i.next();
            retval += "wave " + w.getBaseHealth() + " " + w.getBasePrice() + "\n";
            for (Iterator<String[]> ins = w.getNameStrings().iterator(); ins.hasNext();) {
                String[] strings = ins.next();
                for (int j = 0; j < strings.Length; j++) {
                    retval += strings[j] + " ";
                }
                retval += "\n";
            }
            retval += "\n";
        }

        return retval;
#else
			return "";
#endif
		}
	}
}
